﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.Generic;

namespace ManPowerEngine
{
	/// <summary>
	/// Image Renderer component of a game object.
	/// </summary>
	public class ImageRenderer : RendererBase
	{
		public int DownloadPercentage;
		private Rectangle mRect;
		/// <summary>
		/// Image Brush, storing the image information
		/// </summary>
		private ImageBrush mImageBrush;
		/// <summary>
		/// The writable bitmap. the pixel access of this image
		/// </summary>
		private WriteableBitmap mWritableBitmap;
		private static ImageDownloadTracker mDownloadTracker = new ImageDownloadTracker();
		private WebClient mWebClient;
        /// <summary>
        /// initialize a image renderer
        /// </summary>
        /// <param name="uri">URI of this image</param>
        /// <param name="width">Width in pixel</param>
        /// <param name="height">Height in pixel</param>
        /// <param name="zindex">Z-Index. The bigger the higher.</param>
        public ImageRenderer(string uri, int width, int height, int zindex)
            :this(uri, width, height, zindex, false)
        {
        }

		/// <summary>
		/// initialize a image renderer
		/// </summary>
		/// <param name="uri">URI of this image</param>
		/// <param name="width">Width in pixel</param>
		/// <param name="height">Height in pixel</param>
		/// <param name="zindex">Z-Index. The bigger the higher.</param>
        /// <param name="scale">Should the image be scaled to fill or not</param>
		/// <param name="remote">if this resource is local or not</param>
		public ImageRenderer(string uri, int width, int height, int zindex, bool scale, bool remote)
			: base(width, height, zindex)
		{
			mRect = new Rectangle();
			{
				mRect.Height = Height;
				mRect.Width = Width;
			}
			mImageBrush = new ImageBrush
			{
				Stretch = scale ? Stretch.Fill : Stretch.None,
				AlignmentX = AlignmentX.Left,
				AlignmentY = AlignmentY.Top
			};
			LoadImage(uri, remote);
			mRect.Fill = mImageBrush;
			mElement = mRect;
		}

		/// <summary>
		/// Load/reload image from the URI
		/// </summary>
		/// <param name="uri">URI of the image</param>
		/// <param name="remote">Inside XAP or on the remote side</param>
		public void LoadImage(string uri, bool remote){
			if (uri.Length != 0)
			{
				if (remote)
				{
					if (mWebClient != null)
					{
						mWebClient.CancelAsync();
					}
					mWebClient = new WebClient();
					mWebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(mWebClient_OpenReadCompleted);
					mWebClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(mWebClient_DownloadProgressChanged);
					mWebClient.OpenReadAsync(new Uri(uri, UriKind.Relative));
					mDownloadTracker.Downloading.Add(this);
				}
				else
				{
					ImageSource img;
					img = ResourceHelper.GetBitmap(uri, RenderSystem.msAssemblyName);
					if (img == null)
						throw new ManPowerException("The image '" + uri + "' can not be found.");
					mImageBrush.ImageSource = img;
					DownloadPercentage = 100;
				}
			}
		}

		void mWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
		{
			DownloadPercentage = e.ProgressPercentage;
		}

		void mWebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
		{
			if (!e.Cancelled)
			{
				if (e.Error == null)
				{
					BitmapImage bi = new BitmapImage();
					bi.SetSource(e.Result);
					mImageBrush.ImageSource = bi;
					DownloadPercentage = 100;
					if (mWritableBitmap != null)
					{
						mWritableBitmap = new WriteableBitmap(mRect, null);
					}
				}
				else
				{
					Log.Debug(e.Error);
				}
			}
			mDownloadTracker.Downloading.Remove(this);
			mWebClient = null;
		}

		/// <summary>
		/// initialize a image renderer
		/// </summary>
		/// <param name="uri">URI of this image</param>
		/// <param name="width">Width in pixel</param>
		/// <param name="height">Height in pixel</param>
		/// <param name="zindex">Z-Index. The bigger the higher.</param>
        /// <param name="scale">Should the image be scaled to fill or not</param>
		public ImageRenderer(string uri, int width, int height, int zindex, bool scale) :
			this(uri, width, height, zindex, scale, false) { }

		/// <summary>
		/// Pixel Accessibility of this image
		/// </summary>
		public bool PixelAccessibility
		{
			get
			{
				return (mWritableBitmap != null);
			}
			set
			{
				if (value)
				{
					// set up writable bitmap
					if (mWritableBitmap == null)
					{
						mWritableBitmap = new WriteableBitmap(mRect, null);
					}
				}
				else
				{
					// remove writable bitmap
					if (mWritableBitmap != null)
						mWritableBitmap = null;
				}
			}
		}

		/// <summary>
		/// Get the color of a pixel when Pixel Accessibility is activated.
		/// </summary>
		/// <param name="x">Pixel X</param>
		/// <param name="y">Pixel Y</param>
		/// <returns>The color of this pixel</returns>
		public Color GetPixel(int x, int y)
		{
			if (mWritableBitmap == null)
				throw new ManPowerException("Pixel Accessibility hasn't been activated.");
			int c = mWritableBitmap.Pixels[y * mWritableBitmap.PixelWidth + x];
			return Color.FromArgb((byte)(c >> 24), (byte)(c >> 16), (byte)(c >> 8), (byte)(c));
		}

		/// <summary>
		/// Get the color of a pixel when Pixel Accessibility is activated.
		/// </summary>
		/// <param name="pos">The position of the pixel</param>
		/// <returns>The color of this pixel</returns>
		public Color GetPixel(Vector pos)
		{
			return GetPixel((int)pos.X, (int)pos.Y);
		}

		/// <summary>
		/// Set the color of a pixel when Pixel Accessibility is activated.
		/// </summary>
		/// <param name="x">Pixel X</param>
		/// <param name="y">Pixel Y</param>
		/// <param name="pixel">The color of this pixel</param>
		public void SetPixel(int x, int y, Color pixel)
		{
			if (mWritableBitmap == null)
				throw new ManPowerException("Pixel Accessibility hasn't been activated.");
			int c = ((int)pixel.A << 24) | ((int)pixel.R << 16) | ((int)pixel.G << 8) | ((int)pixel.B);
			mWritableBitmap.Pixels[y * mWritableBitmap.PixelWidth + x] = c;
		}

		/// <summary>
		/// Set the color of a pixel when Pixel Accessibility is activated.
		/// </summary>
		/// <param name="pos">The position of the pixel</param>
		/// <param name="pixel">The color of this pixel</param>
		public void SetPixel(Vector pos, Color pixel)
		{
			SetPixel((int)pos.X, (int)pos.Y, pixel);
		}

		/// <summary>
		/// Get the static download percentage tracker
		/// </summary>
		public static ImageDownloadTracker DownloadTracker
		{
			get
			{
				return mDownloadTracker;
			}
		}
	}

	public class ImageDownloadTracker
	{
		public List<ImageRenderer> Downloading = new List<ImageRenderer>();
		/// <summary>
		/// Overall downloading percentage
		/// </summary>
		public int Percentage
		{
			get
			{
				if (Downloading.Count == 0)
					return 100;
				int total = 0;
				foreach (ImageRenderer ir in Downloading)
				{
					total += ir.DownloadPercentage;
				}
				return total / Downloading.Count;
			}
		}

		public override string ToString()
		{
			return string.Format("{0} Jobs: {1}%", Downloading.Count, Percentage);
		}
	}
}
